import router from '@/router'
import { getToken } from '@/utils/token-util'
import dayjs from 'dayjs'
import { EleMessage } from 'ele-admin-plus/es'
import { ElDivider, ElMessageBox } from 'element-plus'
import { ref } from 'vue'

import request from './request'

/**
 * big.js https://mikemcl.github.io/big.js/#abs
 * abs,取绝对值。
 * cmp,compare的缩写,即比较函数。
 * eq,equal的缩写,即相等比较。
 * gt,大于。
 * gte,小于等于,e表示equal。
 * lt,小于。
 * lte,小于等于,e表示equal。
 * plus,加法。
 * minus,减法。
 * times,乘法。
 * div,除法。
 * mod,取余。
 * sqrt,开方。
 * pow,次方。
 * prec,按精度舍入,参数表示整体位数。
 * round,按精度舍入,参数表示小数点后位数。
 * toExponential,转化为科学计数法,参数代表精度位数。
 * toFied,补全位数,参数代表小数点后位数。
 * toJSON和toString,转化为字符串。
 * toPrecision,按指定有效位数展示,参数为有效位数。
 * toNumber,转化为JavaScript中number类型。
 * valueOf,包含负号（如果为负数或者-0）的字符串。
 */
export { Big } from './big.js'

export { useActivatedReload } from './reload-activated.js'
/**
 * 刷新指定页面的table数据
 */
export { useTableReload } from './reload-page-tab.js'

export const spacer = h(ElDivider, { direction: 'vertical' })

/**
 * 表单 hook
 */
export { useFormData } from './use-form-data.js'

export { usePageTab } from './use-page-tab'

export { cloneDeep } from 'lodash-es'

export function openPage(path) {
  router.push(path)
}

export function sleep(delaytime) {
  return new Promise(resolve => setTimeout(resolve, delaytime))
}

export const Http = [
  'get',
  'post',
  'put',
  'delete',
].reduce((acc, cur) => {
  const fn = (url, data = {}, config) => {
    return ['get', 'delete'].includes(cur)
      ? request[cur](url, {
          params: data,
          ...config,
        })
      : request[cur](url, data, config)
  }
  acc[cur] = fn
  // 生成无需try-catch的异步处理,方法名为awaitGet,awaitPost,awaitPut,awaitDelete
  const awaitFn = `await${cur.charAt(0).toUpperCase()}${cur.slice(1)}`
  acc[awaitFn] = awaitTo(fn)
  return acc
}, {})

export const get = Http.get

export const post = Http.post

/**
 * 根据接口获取列表数据 format参数可格式化数据
 * @param {string} url 接口地址
 * @param {object} data 传参
 * @param {Function} format 格式化数据
 * @returns {Array} 接口数据
 */
export function useGetList(url, data, format) {
  const list = ref([])
  const error = ref(null)
  const loading = ref(false)

  onMounted(async () => {
    loading.value = true
    try {
      const res = await (typeof url === 'function' ? url(data) : Http.get(url, data))
      if (res?.success)
        list.value = typeof format === 'function' ? format(res.data) : res.data
      else
        error.value = res.msg
    }
    catch (err) {
      error.value = err
    }
    finally {
      loading.value = false
    }
  })
  return [list, error, loading]
}

/**
 *  消息提示
 *  兼容EleMessage对象调用模式,如message.success('xxx')
 * @param {string} content 消息文字
 * @param {string|number|boolean} type 消息类型
 * @param {Function} callback 回调函数
 * @returns
 */
export const message = Object.assign(
  (content, type = 'error', callback) => {
    const options = {
      success: '操作成功',
      error: '操作失败',
      info: '操作信息',
      warning: '操作警告',
      loading: '加载中',
      closeAll: '加载中',
    }
    const methods = Object.keys(options)

    if (!methods.includes(type)) {
      if (type === true || type === 0)
        type = 'success'

      else
        type = 'error'
    }
    if (type === 'success' && typeof callback === 'function')
      callback()

    return EleMessage[type](content || options[type])
  },
  EleMessage,
)

/**
 * 消息弹框,用于消息提示、确认消息和提交内容
 * @param {string} content 内容
 * @param {string} title 标题
 * @param {object} options 配置参数
 * @returns
 */
export function confirm(content, title, options) {
  if (!options) {
    options = {}
  }
  if (!options.type)
    options.type = 'warning'

  return new Promise((resolve, reject) => {
    ElMessageBox.confirm(
      content,
      title,
      options,
    )
      .then(res => resolve(res))
      .catch(err => reject(err))
  })
}

/**
 * 格式化时间
 * @param {string | number} time 时间
 * @param {string} display 格式化规则
 * @returns {string} 格式化的时间
 */
export function formatDate(time, display = 'YYYY-MM-DD HH:mm:ss') {
  if (typeof time === 'number' && time.toString().length === 10)
    time *= 1000

  return dayjs(time || undefined).format(display)
}

/**
 *
 * @param {string} url 接口地址
 * @param {object} data 参数
 * @param {Function} callback 回调函数
 */
export function doSwitch(url, data = {}, callback) {
  Http.post(url, data).then((res) => {
    EleMessage[res.status](res.msg)
    if (res.success && callback)
      callback()
  })
}

/**
 * 省略字符串
 * @param {string} str 目标字符串
 * @param {number} start 截取开始位置
 * @param {number} end 截取结束位置
 * @param {string} mid 中间替换字符串
 * @returns
 */
export function ellipsisStr(str, start = 2, end = 2, mid = '*') {
  if (str.length < (start + end))
    return str
  return str.slice(0, start) + mid + str.slice(-end)
}

/**
 * 根据接口获取列表数据 format参数可格式化数据
 * @param {Array} data
 * @param {Array | string} find
 * @param {string} key
 * @param {string} name
 * @returns
 */
export function getFiled(data, find, key = 'id', name = 'name') {
  if (Array.isArray(find))
    return data.filter(v => find.includes(v[key])).map(v => v[name])

  else
    return data.find(v => find === v[key])?.[name] ?? ''
}

/**
 * 异步处理 无需try-catch
 * @param {promise} promise
 * @param {string} errMsg
 * @returns {Array}
 */
export function awaitTo(promise, errMsg) {
  if (typeof promise?.then === 'function') {
    return promise.then(
      // 成功,返回[null,响应结果]
      data => [null, data],
    ).catch((err) => {
      // 失败,返回[错误信息,undefined]
      if (errMsg) {
        const parsedError = Object.assign({}, err, errMsg)
        return [parsedError, undefined]
      }
      return [err, undefined]
    })
  }
  else {
    return async function (...args) {
      try {
        return [null, await promise.apply(this, args)]
      }
      catch (err) {
        return [err]
      }
    }
  }
}

export function btnDisabled(selections, key) {
  return !selections?.length || selections.some(v => v[key] !== 1)
}

/**
 * 求和商品多单位数量
 * @param {Array} list 商品多单位数量
 * @returns {number}
 */
export function totalUnit(list = []) {
  if (!Array.isArray(list)) {
    return 0
  }
  return list.reduce((acc, cur) => {
    return utils.numAdd(acc, utils.numMulti(cur.num, cur.ratio))
  }, 0)
}

export function getExport(api, formObj) {
  formObj.token = `Bearer ${getToken()}`

  const query = new URLSearchParams(formObj).toString()

  const url = `${import.meta.env.VITE_API_URL}${api}?${query}`

  window.open(url)
}

export function getType(value, types, def = 'primary') {
  const obj = {
    primary: [],
    warning: ['DB', 'DS', 'ED'],
    success: ['XS', 'YS', 'YR', 'YP'],
    danger: ['NS', 'NR', 'DJ', 'DY', 'DT', 'DP', 'BY', 'ND', 'FS', 'WC'],
    info: ['CD'],
  }
  return Object.assign(
    Object.keys(obj).reduce((acc, cur) => {
      obj[cur].forEach(v => acc[v] = cur)
      return acc
    }, {}),
    types,
  )[value] ?? def
}

export function isJsonString(value, output = false) {
  try {
    const data = JSON.parse(value)
    return output ? data : true
  }
  catch (error) {
    return false
  }
}

export function encodeFiles(data) {
  return Array.isArray(data) && data.length ? JSON.stringify(data.map(d => ({ ...d, url: d.filepath, name: d.filepath }))) : ''
}

export function decodeFiles(data) {
  data = isJsonString(data, true)
  return Array.isArray(data)
    ? data.map(f => ({
        filepath: f.filepath || f.url || '',
        filename: f.filename || f.name || '',
        name: f.name.slice((f.filename || f.name).lastIndexOf('\\') + 1),
      }))
    : []
}

export function hasActs(data) {
  const keys = Object.keys(data)
  return keys.some(k => k.startsWith('is_') && data[k] === 1)
}

// 乘法函数,用来得到精确的乘法结果
// 说明：javascript的乘法结果会有误差,在两个浮点数相乘的时候会比较明显。这个函数返回较为精确的乘法结果。
// 调用：$h.Mul(arg1,arg2)
// 返回值：arg1乘以arg2的精确结果
export function numMulti(arg1, arg2) {
  arg1 = Number.parseFloat(arg1) || 0
  arg2 = Number.parseFloat(arg2) || 0
  let m = 0
  const s1 = arg1.toString()
  const s2 = arg2.toString()
  try {
    m += s1.split('.')[1].length
  }
  catch (e) { }
  try {
    m += s2.split('.')[1].length
  }
  catch (e) { }
  return Number(s1.replace('.', '')) * Number(s2.replace('.', '')) / 10 ** m
}

// 除法函数,用来得到精确的除法结果
// 说明：javascript的除法结果会有误差,在两个浮点数相除的时候会比较明显。这个函数返回较为精确的除法结果。
// 调用：$h.Div(arg1,arg2)
// 返回值：arg1除以arg2的精确结果
export function numDiv(arg1, arg2) {
  arg1 = Number.parseFloat(arg1)
  arg2 = Number.parseFloat(arg2)
  let t1 = 0
  let t2 = 0
  try {
    t1 = arg1.toString().split('.')[1].length
  }
  catch (e) { }
  try {
    t2 = arg2.toString().split('.')[1].length
  }
  catch (e) { }
  const r1 = Number(arg1.toString().replace('.', ''))
  const r2 = Number(arg2.toString().replace('.', ''))
  return numMulti(r1 / r2, 10 ** (t2 - t1))
}

// 加法函数,用来得到精确的加法结果
// 说明：javascript的加法结果会有误差,在两个浮点数相加的时候会比较明显。这个函数返回较为精确的加法结果。
// 调用：$h.Add(arg1,arg2)
// 返回值：arg1加上arg2的精确结果
export function numAdd(arg1, arg2) {
  arg2 = Number.parseFloat(arg2)
  let r1, r2
  try {
    r1 = arg1.toString().split('.')[1].length
  }
  catch (e) {
    r1 = 0
  }
  try {
    r2 = arg2.toString().split('.')[1].length
  }
  catch (e) {
    r2 = 0
  }
  const m = 100 ** Math.max(r1, r2)
  return (numMulti(arg1, m) + numMulti(arg2, m)) / m
}
// 减法函数,用来得到精确的减法结果
// 说明：javascript的加法结果会有误差,在两个浮点数相加的时候会比较明显。这个函数返回较为精确的减法结果。
// 调用：$h.Sub(arg1,arg2)
// 返回值：arg1减去arg2的精确结果
export function numSub(arg1, arg2) {
  arg1 = Number.parseFloat(arg1)
  arg2 = Number.parseFloat(arg2)
  let r1, r2
  try {
    r1 = arg1.toString().split('.')[1].length
  }
  catch (e) {
    r1 = 0
  }
  try {
    r2 = arg2.toString().split('.')[1].length
  }
  catch (e) {
    r2 = 0
  }
  const m = 10 ** Math.max(r1, r2)
  // 动态控制精度长度
  const n = (r1 >= r2) ? r1 : r2
  return ((numMulti(arg1, m) - numMulti(arg2, m)) / m).toFixed(n)
}
